﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConnectFour
{
    class Board
    {
        const int ROWS = 6, COLUMNS = 7;
        int[] remaining;

        Checker[,] board;
        int[] nextRow = new int[7];

        public Board()
        {
            board = new Checker[ROWS, COLUMNS];

            for (int j = 0; j < COLUMNS; j++)
            {
                for (int i = 0; i < ROWS; i++)
                {
                    board[i, j] = Checker.Empty;
                }
                nextRow[j] = 0;
            }
            remaining = new int[3];
            remaining[(int)Checker.Red] = remaining[(int)Checker.Black] = 21;
            
        }

        /// <summary>
        /// Checks whether a column can still be played in
        /// </summary>
        /// <param name="column">the column to check</param>
        /// <returns>true if more checkers can be played in this column, false otherwise</returns>
        public bool Playable(int column)
        {
            return column >= 0 && column < COLUMNS && board[ROWS - 1, column] == Checker.Empty;
        }

        public int NextRow(int column)
        {
            return nextRow[column];
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public Checker this[int a, int b]
        {
            get{return board[a, b];}
        }

        public int[] Remaining
        {
            get { return remaining; }
        }

        

        /// <summary>
        /// Makes a move on the board.
        /// </summary>
        /// <param name="checker">which checker piece should be played</param>
        /// <param name="column">the column to play in</param>
        /// <returns>the row the checker was played in. If it could not be played, returns -1</returns>
        public int Play(Checker checker, int column)
        {
            if (!Playable(column))
            {
                return -1;
            }

            remaining[(int)checker]--;
            nextRow[column]++;

            for (int i = 0; i < ROWS; i++)
            {
                if (board[i, column] == Checker.Empty)
                {
                    //flags the checker as moving until UI is done animating
                    board[i, column] = checker | Checker.Moving;
                    return i;
                }
            }

            //this line will never be reached.
            return 0;
        }

        /// <summary>
        /// checks for a winner
        /// </summary>
        /// <param name="winner">the winning checker</param>
        /// <returns>true if a winner was found, false otherwise</returns>
        public bool CheckForWinner(out Checker winner)
        {
            //vertical wins
            for (int j = 0; j < COLUMNS; j++)
            {
                //ROWS-3 because we check vertically
                for (int i = 0; i < ROWS - 3; i++)
                {
                    if ((winner = (board[i, j] & board[i + 1, j] & board[i + 2, j] & board[i + 3, j]) & Checker.Color) != Checker.Empty)
                    {
                        board[i, j] |= Checker.Winner;
                        board[i + 1, j] |= Checker.Winner;
                        board[i + 2, j] |= Checker.Winner;
                        board[i + 3, j] |= Checker.Winner;
                        return true;
                    }
                }
            }

            //horizontal wins
            for (int j = 0; j < COLUMNS-3; j++)
            {
                for (int i = 0; i < ROWS; i++)
                {
                    if ((winner = (board[i, j] & board[i, j + 1] & board[i, j + 2] & board[i, j + 3]) & Checker.Color) != Checker.Empty)
                    {
                        board[i, j] |= Checker.Winner;
                        board[i, j + 1] |= Checker.Winner;
                        board[i, j + 2] |= Checker.Winner;
                        board[i, j + 3] |= Checker.Winner;
                        return true;
                    }
                }
            }

            //postive diagonal wins
            for (int j = 0; j < COLUMNS - 3; j++)
            {
                for (int i = 0; i < ROWS - 3 ; i++)
                {
                    if ((winner = (board[i, j] & board[i + 1, j + 1] & board[i + 2, j + 2] & board[i + 3, j + 3]) & Checker.Color) != Checker.Empty)
                    {
                        board[i, j] |= Checker.Winner;
                        board[i + 1, j + 1] |= Checker.Winner;
                        board[i + 2, j + 2] |= Checker.Winner;
                        board[i + 3, j + 3] |= Checker.Winner;
                        return true;
                    }
                }
            }

            //negative diagonal wins
            for (int j = 3; j < COLUMNS ; j++)
            {
                for (int i = 0; i < ROWS - 3; i++)
                {
                    if ((winner = (board[i, j] & board[i + 1, j - 1] & board[i + 2, j - 2] & board[i + 3, j - 3]) & Checker.Color) != Checker.Empty)
                    {
                        board[i, j] |= Checker.Winner;
                        board[i + 1, j - 1] |= Checker.Winner;
                        board[i + 2, j - 2] |= Checker.Winner;
                        board[i + 3, j - 3] |= Checker.Winner;
                        return true;
                    }
                }
            }
            
            winner = Checker.Empty;

            //check if board is full
            for (int j = 0; j < COLUMNS; j++)
            {
                if (Playable(j))
                {
                    return false;
                }
            }

            //draw case
            //board is full, so return true with winner being set to Checker.Empty
            return true;
        }

        public override String ToString()
        {
            StringBuilder sb = new StringBuilder();
            for (int i = ROWS-1; i >=0; i--)
            {
                for (int j = 0; j < COLUMNS; j++)
                {
                    switch (board[i, j] & Checker.Color)
                    {
                        case Checker.Red:
                            sb.Append('R');
                            break;
                        case Checker.Black:
                            sb.Append('B');
                            break;
                        case Checker.Empty:
                            sb.Append('-');
                            break;
                    }
                }
                sb.Append('\n');
            }
            return sb.ToString();
        }
    }
}
